﻿using System.Collections.Generic;
using System.IO;
using System.Linq;
using Common;
using ImageProcessing.Common;
using ProtoBuf;

namespace ImageProcessing.Storage
{
    public abstract class PersistanceDB : IPersistanceDB
    {
        protected const string PointsFileExtension = ".points";

        private readonly Dictionary<TagInfo, List<PointInfo>> _cachedPoints = new Dictionary<TagInfo, List<PointInfo>>();

        protected abstract Stream FileStream(string fileName, FileMode fileMode, FileAccess fileAccess);

        public abstract string[] GetAllPointsFiles();

        protected abstract void DeleteFile(string fileName);

        public List<InterestPoint> LoadAllPoints()
        {
            List<PointInfo> result = new List<PointInfo>();
            string[] filesToLoad = GetAllPointsFiles();
            foreach (var fileToLoad in filesToLoad)
            {
                using (Stream stream = FileStream(fileToLoad, FileMode.Open, FileAccess.Read))
                {
                    List<PointInfo> points = Serializer.Deserialize<List<PointInfo>>(stream);

                    if (points.FirstOrDefault() != null)
                        _cachedPoints.Add(points[0].TagInfo, points);

                    result.AddRange(points);
                }
            }

            return PersistanceHelpers.BuildInterestPoints(result);
        }

        public void SavePoints(TagInfo tagInfo, List<InterestPoint> interestPoints)
        {
            using (Stream stream = FileStream(tagInfo.TagId + PointsFileExtension, FileMode.OpenOrCreate, FileAccess.Write))
            {
                List<PointInfo> newPoints = PersistanceHelpers.BuildPointInfos(interestPoints);

                List<PointInfo> cachedPoints;
                if (!_cachedPoints.TryGetValue(tagInfo, out cachedPoints))
                    _cachedPoints[tagInfo] = new List<PointInfo>();

                cachedPoints = _cachedPoints[tagInfo];

                List<PointInfo> uniqueNewPoints = new List<PointInfo>();


                foreach (var pointInfo in newPoints)
                {
                    bool matchFound = false;

                    foreach (var cachedPoint in cachedPoints)
                    {
                        if (pointInfo.Sign == cachedPoint.Sign && pointInfo.DescriptorEqual(cachedPoint.Descriptor))
                        {
                            matchFound = true;
                            break;
                        }
                    }

                    if (!matchFound)
                        uniqueNewPoints.Add(pointInfo);
                }

                cachedPoints.AddRange(uniqueNewPoints);
                Serializer.Serialize(stream, cachedPoints);
                stream.Flush();
                stream.Close();
            }
        }

        public void Clear()
        {
            string[] fileToDelete = GetAllPointsFiles();
            foreach (var fileToLoad in fileToDelete)
            {
                DeleteFile(fileToLoad);
            }
        }
    }
}
